package com.andnnl.template;

import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * Created by chenss on 2024/8/27.
 */
public class TrieJy<T> {
    TrieNode<T> root;

    public TrieJy() {
        root = new TrieNode();
    }

    // public void insert(String keyword, T t) {
    // insert(keyword,t);
    // }

    /**
     * 向字典树中插入一个新的关键字及其关联的数据
     *
     * @param keyword 插入的关键字，将被转换为小写进行存储
     * @param t       与关键字关联的数据，可以是任意类型
     *                <p>
     *                此方法首先将关键字转换为小写，以确保检索的大小写不敏感性
     *                然后，它遍历关键字中的每个字符，沿着字典树向下创建新的节点（如果尚不存在）
     *                最后，在最深的节点上存储关联的数据
     *                如果存在相同的关键字，数据将被收集在相同的节点中
     */
    public void insert(String keyword, T t) {
        // 从根节点开始
        TrieNode<T> currentNode = root;
        // 将关键字转换为小写，以标准化存储和检索
        String normalizedKeyword = keyword.toLowerCase();
        // 遍历关键字中的每个字符
        for (char ch : normalizedKeyword.toCharArray()) {
            // 如果当前字符的子节点不存在，则创建它
            currentNode = currentNode.children.computeIfAbsent(ch, k -> new TrieNode<>());
        }
        // 将数据添加到当前节点的数据列表中，允许一个关键字对应多个数据项
        currentNode.nodeDataList.add(t);
    }

    /**
     * 查找所有的匹配逻辑
     *
     * @param originalText 原始文本，用于后续匹配
     * @param matchFunc    匹配函数，用于判断特定的匹配逻辑。可以为空。
     * @return 返回一个包含所有匹配项的列表
     */
    public List<T> search(String originalText, BiFunction<String, T, Boolean> matchFunc) {
        return search(originalText, matchFunc, true, false);
    }

    public T searchOne(String originalText, BiFunction<String, T, Boolean> matchFunc) {
        List<T> rsList = search(originalText, matchFunc, false, false);
        return rsList.size() > 0 ? rsList.get(0) : null;
    }

    /**
     * 查找所有的匹配逻辑
     *
     * @param originalText 原始文本，用于后续匹配
     * @param matchFunc    匹配函数，用于判断特定的匹配逻辑。可以为空。
     * @param isFindAll    是否查找所有匹配项
     * @param isShortMatch 是否短匹配,true:相当于前缀匹配startWith,false:全文本中匹配，相当于indexOf
     * @return 返回一个包含所有匹配项的列表
     */
    public List<T> search(String originalText, BiFunction<String, T, Boolean> matchFunc, boolean isFindAll,
            boolean isShortMatch) {

        // 用于存储最终匹配结果的集合，使用Set避免重复
        Set<T> rsList = new HashSet<>();
        // 将原始文本转换为小写，以适应后续大小写不敏感的匹配
        String text = originalText.toLowerCase();

        // 遍历文本中的每个字符
        int index = 0;
        while (index < text.length()) {
            // 每次循环开始时，重置当前节点为根节点
            TrieNode<T> node = root;
            // 遍历当前节点开始的每个字符，查找匹配的节点
            for (int i = index; i < text.length(); i++) {
                // 获取当前字符
                char ch = text.charAt(i);
                // 如果当前节点的子节点中不存在该字符，则跳出循环，继续下一个字符的匹配
                if (!node.children.containsKey(ch)) {
                    if (isShortMatch) {
                        return rsList.stream().collect(Collectors.toList());// 短匹配
                    }
                    continue;
                }
                // 找到匹配的节点，更新当前节点为匹配的子节点，继续深入查找
                node = node.children.get(ch);
                // 如果当前节点有关联的数据，并且有指定的匹配函数，则执行匹配逻辑
                if (node.nodeDataList.size() > 0) {
                    if (matchFunc != null) {
                        // 遍历当前节点的所有关联数据，通过匹配函数进行筛选
                        for (T t : node.nodeDataList) {
                            if (matchFunc.apply(originalText, t)) {
                                rsList.add(t);
                                if (!isFindAll) {
                                    return rsList.stream().collect(Collectors.toList());
                                }
                            }
                        }
                    } else {
                        // 如果没有指定匹配函数，则直接将当前节点的所有关联数据添加到结果集中
                        if (!isFindAll) {
                            rsList.add(node.nodeDataList.get(0));// 返回第一个
                            return rsList.stream().collect(Collectors.toList());
                        } else {
                            rsList.addAll(node.nodeDataList);
                        }
                    }
                }
            }
            // 增加索引，以继续下一个字符的匹配
            index++;
        }
        // 将结果集转换为列表形式返回
        return rsList.stream().collect(Collectors.toList());
    }

}

class TrieNode<T> {
    Map<Character, TrieNode<T>> children;
    List<T> nodeDataList;

    public TrieNode() {
        children = new HashMap<>();
        nodeDataList = new ArrayList<>();
    }
}
